﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using NLog;
using O2DESNet;
using ShkSimulation.core.dao.input.resource;
using ShkSimulation.core.dao.output.result;
using ShkSimulation.core.optimization;
using ShkSimulation.core.optimization.extractor;
using ShkSimulation.core.optimization.impl;
using ShkSimulation.core.template;
using ShkSimulation.core.util;
using ShkSimulation.desktop.component.tools;
using ShkSimulation.desktop.setting;
using ShkSimulation.carsharing.impl;

namespace ShkSimulation.desktop.component {
    public sealed partial class OptimizationControl : UserControl, ResetSimulation {

        private SimulationApplication application;
        private Network Network { get; set; }
        private NetworkResultHolder ResultHolder { get; set; }
        private NetworkEnvironment NetworkInputData { get; set; }
        
        private CellPointBindingList CellPointDisplayBindingList { get; set; }
        private TargetValueRowBindingList TargetValueBindingList { get; set; }
        private NamedCellPointGroup DecisionCellPointGroup { get; set; }
        private NamedCellPointGroup TargetCellPointGroup { get; set; }
                
        #region resources
        private IReadOnlyDictionary<string, Resource> Resources => resources.AsReadOnly();
        private Dictionary<string, Resource> resources = new Dictionary<string, Resource>();
        private BindingList<string> resourceKeys = new BindingList<string>();
        #endregion

        public OptimizationControl() {
            InitializeComponent();
            this.Dock = DockStyle.Fill;
            this.SelectedPointGridView.AutoGenerateColumns = false;
            this.SelectedTargetRowGridView.AutoGenerateColumns = false;
            this.CellPointDisplayBindingList = new CellPointBindingList(SelectedPointGridView);
            this.TargetValueBindingList = new TargetValueRowBindingList(SelectedTargetRowGridView);

            ResultValueComboBox.DataSource = TargetValueBindingList;
            ResultValueComboBox.FormatString = TargetValueRowDisplay.UniqueKeyName;
            this.Load += Test;
            
        }

        private void Test(object sender, EventArgs e) {
            CellPoint point = new CellPoint("x1", 0, 4, "15");
            CellPointDisplay display = new CellPointDisplay("station", point);
            CellPointDisplayBindingList.Add(display);
            DecisionCellPointGroup.AddNamedCellPoint("station", point);
            
            point = new CellPoint("x2", 1, 4, "15");
            display = new CellPointDisplay("station", point);
            CellPointDisplayBindingList.Add(display);
            DecisionCellPointGroup.AddNamedCellPoint("station", point);   
            
            point = new CellPoint("x3", 2, 4, "18");
            display = new CellPointDisplay("station", point);
            CellPointDisplayBindingList.Add(display);
            DecisionCellPointGroup.AddNamedCellPoint("station", point);     
            
            point = new CellPoint("x4", 3, 4, "20");
            display = new CellPointDisplay("station", point);
            CellPointDisplayBindingList.Add(display);
            DecisionCellPointGroup.AddNamedCellPoint("station", point);            
            
            // point = new CellPoint("x5", 4, 4, "20");
            // display = new CellPointDisplay("station", point);
            // CellPointDisplayBindingList.Add(display);
            // DecisionCellPointGroup.AddNamedCellPoint("station", point);
        }

        public void SetSimulationApplication(SimulationApplication app) {
            Reset();
            this.application = app;
            this.Network = application.SimulationNetwork;
            this.NetworkInputData = application.NetworkInputData;
            this.ResultHolder = application.ResultHolder;
            this.Name = application.SimulationName;
            
            application.AfterInitSuccess += AfterInitSuccess;
            application.AfterRunCompleted += AfterRunCompleted;

        }

        public void Reset() {
            this.application = null;
            this.Network = null;
            this.NetworkInputData = null;
            this.ResultHolder = null;
            this.application = null;
            DecisionCellPointGroup = new NamedCellPointGroup();
            TargetCellPointGroup = new NamedCellPointGroup();
            CellPointDisplayBindingList.Clear();
            TargetValueBindingList.Clear();
            resourceKeys.Clear();
            resourceKeys.Clear();
            ResourceTabControl.TabPages.Clear();
            resources.Clear();
        }

        private void AfterInitSuccess(SimulationInfo simulationInfo) {
            PrepareExtractData();
            foreach ((var key, var value) in resources) {
                TabPage page = new TabPage(key);
                ResourceTabControl.TabPages.Add(page);
                DataGridView dataGridView = CreateDataGridViewByResource(value);
                dataGridView.CellDoubleClick += DataGridViewOnCellContentDoubleClick;
                page.Controls.Add(dataGridView);
            }
        }

        private void AfterRunCompleted(SimulationInfo simulationInfo){
            Result result = simulationInfo.Result;
            DataTableResourceResult resourceResult = new DefaultDataTableResourceResult(result);
            TabPage page = new TabPage("结果");
            DataGridView dataGridView = CreateDataGridViewByResource(resourceResult);
            dataGridView.CellDoubleClick += ResultDoubleClick;
            page.Controls.Add(dataGridView);
            this.Invoke(new Action(() => {
                ResourceTabControl.TabPages.Add(page);
            }));
            void ResultDoubleClick(object sender, DataGridViewCellEventArgs e) {
                int row = e.RowIndex;
                if (row < 0) {
                    return;
                }
                CellPoint value = resourceResult.GetValuePosition(row);
                if (string.IsNullOrEmpty(value.CurrentValue)){
                    MessageBox.Show($@"数据不是有效的, {value.Name}:{value.CurrentValue}", @"提示");
                    return;
                }
                string uniqueKey = SingleTextInputForm.GetInput("输入框", "请输入评估变量唯一标识:");
                if (string.IsNullOrEmpty(uniqueKey)) {
                    return;
                }
                if (Regex.Match(uniqueKey, "[\\d]").Success) {
                    MessageBox.Show($@"不能有数字噢噢, {uniqueKey}");
                    return;
                }
                string defaultTableKey = "Result";
                if (TargetCellPointGroup.IsPointRepeat(defaultTableKey, uniqueKey, row, 0)) {
                    MessageBox.Show($@"当前表格数据重复选择, {uniqueKey}:{row}");
                    return;
                }
                value = new CellPoint(uniqueKey, value.Row, value.Column, value.CurrentValue);
                TargetValueRowDisplay display = new TargetValueRowDisplay(uniqueKey, value);
                TargetValueBindingList.Add(display);
                TargetCellPointGroup.AddNamedCellPoint(defaultTableKey, value);
            }
        }

        private DataGridView CreateDataGridViewByResource(Resource resource) {
            DataTable dataTable = resource.GetDataTable();
            DataGridView dataGridView = new DataGridView();
            dataGridView.AllowUserToAddRows = false;
            dataGridView.AllowUserToDeleteRows = false;
            dataGridView.AllowUserToOrderColumns = true;
            dataGridView.Name = resource.Uniquekey;
            dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
            dataGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
            dataGridView.BackgroundColor = SystemColors.Control;
            dataGridView.BorderStyle = BorderStyle.None;
            dataGridView.Dock = DockStyle.Fill;
            dataGridView.ReadOnly = true;
            dataGridView.DataSource = dataTable;
            return dataGridView;
        }

        private void DataGridViewOnCellContentDoubleClick(object sender, DataGridViewCellEventArgs e) {
            int row = e.RowIndex;
            int column = e.ColumnIndex;
            if (row < 0 || column < 0) {
                return;
            }
            DataGridView dataGridView = (DataGridView)sender;
            string resourceUniqueKey = dataGridView.Name;
            string uniqueKey = SingleTextInputForm.GetInput("输入框", "请输入决策变量唯一标识:");
            if (string.IsNullOrEmpty(uniqueKey)) {
                return;
            }
            if (DecisionCellPointGroup.IsPointRepeat(resourceUniqueKey, uniqueKey, row, column)) {
                MessageBox.Show($@"当前表格数据重复选择, {uniqueKey},{row}:{column}");
                return;
            }
            DataTable dataTable = (DataTable)dataGridView.DataSource;
            CellPoint point = new CellPoint(uniqueKey, row, column, dataTable.Rows[row][column]?.ToString());
            CellPointDisplay display = new CellPointDisplay(resourceUniqueKey, point);
            CellPointDisplayBindingList.Add(display);
            DecisionCellPointGroup.AddNamedCellPoint(resourceUniqueKey, point);
        }

        private void PrepareExtractData() {
            resourceKeys.Clear();
            ResourceTabControl.TabPages.Clear();
            IReadOnlyDictionary<string, Resource> readOnlyDictionary = NetworkInputData.AllResources;
            resources = new Dictionary<string, Resource>(readOnlyDictionary);
            foreach (var key in resources.Keys) {
                resourceKeys.Add(key);
            }
        }

        private void InsertButton_Click(object sender, EventArgs e) {
            if (ResultValueComboBox.SelectedItem is TargetValueRowDisplay display) {
                Insert(display.UniqueKey);
            } else {
                MessageBox.Show("有点奇怪");
            }
        }
        
        private void Insert(string text) {            
            if (TargetFunctionTextBox.SelectionLength > 0) {
                TargetFunctionTextBox.SelectionStart = TargetFunctionTextBox.SelectionStart + TargetFunctionTextBox.SelectionLength;
            }
            int index = TargetFunctionTextBox.SelectionStart;
            TargetFunctionTextBox.Focus();
            TargetFunctionTextBox.SelectedText = text;
        }

        private void CalculateButton_Click(object sender, EventArgs e) {
            string formula = TargetFunctionTextBox.Text;
            if (string.IsNullOrEmpty(formula)) {
                MessageBox.Show("公式为空", "提示");
                return;
            }
            var valueMap = TargetValueBindingList.GetValueMap();
            (bool success, double value) = MathUtil.Execute(formula, valueMap);
            if (success) {
                this.ValueTextBox.Text = value.ToString();
            } else {
                this.ValueTextBox.Text = @"解析错误";
            }
        }

        private void RunOptimizationButton_Click(object sender, EventArgs e) {
            RunOptimizationButton.Enabled = false;
            if (ConfigManager.UserRunConfig.真实时间) {
                Console.WriteLine(@"切换设置为仿真时间:{0}", ConfigManager.UserRunConfig.仿真时长);
                ConfigManager.UserRunConfig.真实时间 = false;
            }
            Console.WriteLine(@"开始优化! ");
            Console.WriteLine(@"决策变量：{0}", DecisionCellPointGroup);
            Console.WriteLine(@"目标函数变量：{0}", TargetCellPointGroup);            
            Console.WriteLine(@"目标函数：{0}", TargetFunctionTextBox.Text);
            OptimizeWorker.RunWorkerAsync();
            // while (OptimizeWorker.IsBusy) {
            //     if ((stopwatch.ElapsedMilliseconds / 1000) % 2 == 0) {
            //         Console.WriteLine(@"正在运行优化，已持续运行：{0}", stopwatch.Elapsed);
            //     }
            // }
        }

        private void OptimizeWorker_DoWork(object sender, DoWorkEventArgs e) {
            Stopwatch stopwatch = Stopwatch.StartNew();
            string formula = TargetFunctionTextBox.Text;
            OptimizationConfig config = OptimizationConfig.Create(ConfigManager.UserRunConfig.仿真开始时刻, 
                ConfigManager.UserRunConfig.仿真时长, 
                ConfigManager.UserRunConfig.仿真时长);
            DefaultOptimizationNetwork optimization = new DefaultOptimizationNetwork(application.SimulationNetwork, 
                AlgorithmControl.GetAlgorithm(),
                new FormulaEvaluationStrategy(formula),
                DecisionCellPointGroup,
                TargetCellPointGroup,
                config);
            LogManager.DisableLogging();
            optimization.Optimize();
            LogManager.EnableLogging();
            string result = string.Format("仿真优化结束，最优种群：{0}\r\n目标函数值：{1}", 
                optimization.BestOne.VariableVector, optimization.BestOne.Valuation);
            Console.WriteLine(result);
            this.Invoke(new Action(() => {
                OptimizationInfoTextBox.Text = result;
                RunOptimizationButton.Enabled = true;
                Console.WriteLine(@"共耗时：{0}", stopwatch.Elapsed);
            }));
        }
    }
}
